package ace.tool.generate.code.impl.resolver.scheme.resolver.api;

import ace.tool.generate.code.impl.manager.common.EntityModelManager;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.ApiProject;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.api.ApiClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.api.AuthorityConstantClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.api.ConstantClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.api.DtoClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.api.EnumClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.dto.CommonInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.dto.ProjectInfo;
import ace.tool.generate.code.impl.resolver.scheme.resolver.ProjectInfoResolver;
import ace.tool.generate.code.impl.service.model.input.GenerateInput;
import ace.tool.generate.code.impl.service.model.input.GenerateModelFieldInput;
import ace.tool.generate.code.impl.service.model.input.GenerateModelInput;
import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;

/**
 * @author caspar
 * @date 2023/11/17 10:34
 */
@RequiredArgsConstructor
public class ApiProjectResolver {
  private final GenerateInput input;
  private final ProjectInfo moduleProjectInfo;
  private final CommonInfo commonInfo;
  private final EntityModelManager entityModelManager = new EntityModelManager();

  public ApiProject resolve() {
    ProjectInfo apiProjectInfo = this.getProjectInfo();

    List<EnumClassInfo> enumClassInfos = this.getEnumClassInfos(apiProjectInfo);
    List<DtoClassInfo> dtoClasses = this.getDtoClasses(apiProjectInfo, enumClassInfos);
    AuthorityConstantClassInfo authorityConstantClassInfo =
        this.getAuthorityConstantClassInfo(apiProjectInfo);

    return ApiProject.builder()
        .info(apiProjectInfo)
        .constantClassInfo(this.getConstantClassInfo(apiProjectInfo))
        .enumClassInfos(enumClassInfos)
        .apiClasses(this.getApiClasses(apiProjectInfo))
        .dtoClasses(dtoClasses)
        .authorityConstantClassInfo(authorityConstantClassInfo)
        .build();
  }

  private AuthorityConstantClassInfo getAuthorityConstantClassInfo(ProjectInfo apiProjectInfo) {
    return AuthorityConstantClassInfoResolver.builder()
        .apiProjectInfo(apiProjectInfo)
        .commonInfo(commonInfo)
        .input(input)
        .build()
        .resolve();
  }

  public ProjectInfo getProjectInfo() {
    return ProjectInfoResolver.builder()
        .projectNamePrefix(input.getProjectNamePrefix())
        .projectNameSuffix(input.getApiProjectNameSuffix())
        .projectName(input.getProjectName())
        .projectRootDir(moduleProjectInfo.getRootDir())
        .projectDisplayName(input.getProjectDisplayName())
        .build()
        .resolve();
  }

  public List<EnumClassInfo> getEnumClassInfos(ProjectInfo apiProjectInfo) {
    return input.getModels().stream()
        .map(
            modelInput ->
                modelInput.getFields().stream()
                    .filter(modelFieldInput -> modelFieldInput.getEnums() != null)
                    .map(
                        modelFieldInput ->
                            this.getEnumClassInfo(modelInput, modelFieldInput, apiProjectInfo))
                    .collect(Collectors.toList()))
        .flatMap(List::stream)
        .collect(Collectors.toList());
  }

  private EnumClassInfo getEnumClassInfo(
      GenerateModelInput modelInput,
      GenerateModelFieldInput modelFieldInput,
      ProjectInfo apiProjectInfo) {

    return EnumClassInfoResolver.builder()
        .apiProjectInfo(apiProjectInfo)
        .commonInfo(commonInfo)
        .modelFieldInput(modelFieldInput)
        .modelInput(modelInput)
        .input(input)
        .build()
        .resolve();
  }

  public ConstantClassInfo getConstantClassInfo(ProjectInfo apiProjectInfo) {
    return ConstantClassInfoResolver.builder()
        .commonInfo(commonInfo)
        .apiProjectInfo(apiProjectInfo)
        .input(input)
        .build()
        .resolve();
  }

  public List<ApiClassInfo> getApiClasses(ProjectInfo apiProjectInfo) {
    return this.input.getModels().stream()
        .map(p -> this.getApiClass(p, apiProjectInfo))
        .collect(Collectors.toList());
  }

  private ApiClassInfo getApiClass(GenerateModelInput modelInput, ProjectInfo apiProjectInfo) {
    return ApiClassInfoResolver.builder()
        .apiProjectInfo(apiProjectInfo)
        .commonInfo(commonInfo)
        .modelInput(modelInput)
        .input(input)
        .build()
        .resolve();
  }

  public List<DtoClassInfo> getDtoClasses(
      ProjectInfo apiProjectInfo, List<EnumClassInfo> enumClassInfos) {

    return this.input.getModels().stream()
        .map(p -> this.getDtoClass(p, apiProjectInfo, enumClassInfos))
        .collect(Collectors.toList());
  }

  private DtoClassInfo getDtoClass(
      GenerateModelInput modelInput,
      ProjectInfo apiProjectInfo,
      List<EnumClassInfo> enumClassInfos) {

    return new DtoClassInfoResolver(input, modelInput, commonInfo, apiProjectInfo, enumClassInfos)
        .resolve();
  }
}
